package com.xinqi.util.sqlsession.sqlsessionarray;

import com.xinqi.common.SystemContents;
import com.xinqi.util.sqlsession.SingleSqlSession;
import com.xinqi.util.sqlsession.SqlSessionUtil;
import com.xinqi.util.sqlsession.mappersqlsession.BlogMapperSqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author xin麒
 * @date 2023/8/24 12:32
 */
public class SqlSessionArray {//每一个线程使用一个sqlsession连接,这个方法是我手写的，待测试
    //这3个参数如果实际真被用到，则初始化；没使用到就不需要初始化，没必要浪费空间
    private byte maxCapacity;
    private volatile List<SingleSqlSession> sqlSessions;
    private AtomicBoolean lock;//true代表当前线程拿到锁，可以为sqlSessions添加新的sqlSession连接；false表示拿不到锁，无法对sqlSessions添加新sqlSession连接

    private void init() {//以双重检索方式初始化参数
        if (sqlSessions == null) {
            synchronized (this) {
                if (sqlSessions == null) {
                    //创建sqlsession的连接数量是主要取决于CPU_COUNT，CPU_COUNT如果没这么大的话，那么sqlsession的连接数应该是可以承受的住的
                    maxCapacity = SystemContents.CPU_COUNT + 1;
                    sqlSessions = new ArrayList<>(maxCapacity);
                    lock = new AtomicBoolean(true);
                }
            }
        }
    }

    private boolean lock() {
        return lock.compareAndSet(true, false);
    }

    private void unlock() {
        lock.getAndSet(true);
    }

    //这个类有没有用得到，实际上就是看方法getSingleSqlSession实际上有没有被使用到。所以说init方法放到这个getSingleSqlSession方法即可
    public SingleSqlSession getSingleSqlSession() {
        init();
        int index = 0;
        while (true) {
            if (index >= maxCapacity) {//超过最大的容量肯定是不可以的，所以得往回找可连接的sqlsession
                index = 0;
                continue;
            }
            //如果此时sqlSessions要进行扩容的考虑了。此时的index < maxCapacity恒成立所以sqlSessions.size() < maxCapacity成立
            if (index == sqlSessions.size()) {
                if (!lock()) {//-----获取锁开始
                    index = 0;//拿不到锁，于是扩容操作就让苦于拿到锁的线程去执行扩容操作
                    continue;
                }
                //以下是拿到锁的对象进行扩容操作
                try {
                    //还需要判断sqlsession是否容量达到最大了。防止刚刚已经扩容到最大时就有一个线程获取锁并进行扩容
                    if (sqlSessions.size() >= maxCapacity) continue;
                    SingleSqlSession singleSqlSession = new SingleSqlSession();
                    singleSqlSession.lock();//这里先提前锁好，不然可能会被其他线程获取到
                    //对于List对象，因为这里只有持有锁的那个线程才可以执行，因此不需要使用Vector等线程安全类型的集合。
                    // 在普通类型加volatile修饰即可，这样子其他线程获取该List对象的属性等值就是最新的了。
                    sqlSessions.add(singleSqlSession);
                    return singleSqlSession;
                } finally {
                    unlock();//最后都要执行锁释放操作，如果有异常就让它往外抛吧，不管了。-----释放锁结束
                }
            }
            SingleSqlSession singleSqlSession = sqlSessions.get(index);//index <=  sqlSessions.size() < maxCapacity的情况
            if (singleSqlSession.lock()) return singleSqlSession;
            index++;
        }
    }

    public void helpGC() {
        if (sqlSessions == null) return;
        for (SingleSqlSession sqlSession : sqlSessions) {
            SqlSession session = sqlSession.getSqlSession();
            if (session != null) session.close();
        }
        if (sqlSessions != null) sqlSessions.clear();
        sqlSessions = null;
    }
}
